package com.ssy.lingxi.report.serviceimpl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.report.api.vo.request.AddOrderVO;
import com.ssy.lingxi.report.dao.report.OrderAddRecordDAO;
import com.ssy.lingxi.report.entity.report.OrderDayReportDO;
import com.ssy.lingxi.report.entity.report.OrderMonthReportDO;
import com.ssy.lingxi.report.model.bo.StatisticsOrderBO;
import com.ssy.lingxi.report.model.vo.response.OrderStatisticsDetailVO;
import com.ssy.lingxi.report.model.vo.response.OrderStatisticsVO;
import com.ssy.lingxi.report.repository.report.OrderDayReportRepository;
import com.ssy.lingxi.report.repository.report.OrderMonthReportRepository;
import com.ssy.lingxi.report.service.IOrderReportService;
import com.ssy.lingxi.report.utils.TimeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 订单报表接口实现
 * @author 伍将
 * @date 2020/11/10
 * @version 2.0.0
 */
@Service
public class OrderReportServiceImpl implements IOrderReportService {

    @Resource
    private OrderMonthReportRepository orderMonthReportRepository;

    @Resource
    private OrderDayReportRepository orderDayReportRepository;

    @Resource
    private OrderAddRecordDAO orderAddRecordDAO;

    private static final Logger logger = LoggerFactory.getLogger(OrderReportServiceImpl.class);

    /**
     * 订单日报表
     * @author 伍将
     * @date 2020/11/10
     * @param startTime:
     * @param endTime:
     **/
    @Transactional
    @Override
    public void orderDayReport(LocalDateTime startTime, LocalDateTime endTime) {
        List<StatisticsOrderBO> orderBOList = orderAddRecordDAO.getStatisticsOrderByDay(startTime, endTime);
        if (orderBOList == null) {
            logger.info("没有查询到订单");
            return;
        }

        logger.info("统计日订单：{}-{}，共{}单，营业额：{}", DateUtil.format(DateUtil.date(startTime), "yyyy-MM-dd HH:mm"),
                DateUtil.format(DateUtil.date(endTime), "yyyy-MM-dd HH:mm"),
                orderBOList.stream().mapToLong(StatisticsOrderBO::getCount).sum(),
                orderBOList.stream().mapToDouble(StatisticsOrderBO::getAmount).sum());

        // 新增前先删除旧数据
        orderDayReportRepository.deleteByDateTimeBetween(startTime, endTime);

        // 新增会员日报表数据
        List<OrderDayReportDO> reportDOList = orderBOList.stream().map(r -> {
            OrderDayReportDO reportDO = new OrderDayReportDO();
            reportDO.setDateTime(r.getDateTime());
            reportDO.setAmount(r.getAmount());
            reportDO.setCount(r.getCount());
            return reportDO;
        }).collect(Collectors.toList());

        orderDayReportRepository.saveAll(reportDOList);
    }

    /**
     * 订单月报表
     * @author 伍将
     * @date 2020/11/10
     * @param startTime:
     * @param endTime:
     **/
    @Transactional
    @Override
    public void orderMonthReport(LocalDateTime startTime, LocalDateTime endTime) {
        List<StatisticsOrderBO> orderBOList = orderAddRecordDAO.getStatisticsOrderByMonth(startTime, endTime);
        if (orderBOList == null) {
            logger.info("没有查询到订单");
            return;
        }

        logger.info("统计月订单：{}-{}，共{}条数据,总共金额{}",
                DateUtil.format(DateUtil.date(startTime), "yyyy-MM-dd HH:mm"),
                DateUtil.format(DateUtil.date(endTime), "yyyy-MM-dd HH:mm"),
                orderBOList.stream().mapToLong(StatisticsOrderBO::getCount).sum(),
                orderBOList.stream().mapToDouble(StatisticsOrderBO::getAmount).sum());

        // 新增前先删除旧数据
        orderMonthReportRepository.deleteByDateTimeBetween(startTime, endTime);

        // 新增会员日报表数据
        List<OrderMonthReportDO> reportDOList = orderBOList.stream().map(r -> {
            OrderMonthReportDO reportDO = new OrderMonthReportDO();
            reportDO.setDateTime(r.getDateTime());
            reportDO.setAmount(r.getAmount());
            reportDO.setCount(r.getCount());
            return reportDO;
        }).collect(Collectors.toList());

        orderMonthReportRepository.saveAll(reportDOList);
    }

    /**
     * 获取订单数据
     * @author 伍将
     * @date 2020/11/10
     * @param user:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.report.model.vo.response.OrderStatisticsVO>
     **/
    @Override
    public Wrapper<OrderStatisticsVO> getOrderList(UserLoginCacheDTO user) {
        OrderStatisticsVO result = new OrderStatisticsVO();

        DecimalFormat format = new DecimalFormat("#.00");

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String startDate;
        String endDate = DateUtil.date().toString("yyyy-MM-dd 23:59:59");
        LocalDateTime endTime = LocalDateTime.parse(endDate, formatter);
        List<String> timeList;

        // 查询近三十天数据
        startDate = DateUtil.offsetMonth(DateUtil.date(), -1).toString("yyyy-MM-dd 00:00:00");
        LocalDateTime monthStartTime = LocalDateTime.parse(startDate, formatter);
        List<OrderDayReportDO> weekDOList = orderDayReportRepository.getAllByDateTimeBetween(monthStartTime, endTime);

        timeList = TimeUtil.getBetweenDays(startDate, endDate);
        List<OrderStatisticsDetailVO> monthList = convertOrderDayStatistics(timeList, weekDOList);
        if (monthList.size() > 0) {

            // 设置三十日数据
            result.setMonthList(monthList);
            result.setMonthCount(monthList.stream()
                    .filter(o -> o.getDateTime().isAfter(monthStartTime) && o.getDateTime().isBefore(endTime))
                    .mapToLong(OrderStatisticsDetailVO::getCount).sum());
            result.setMonthAmount(
                    Double.parseDouble(format.format(
                            monthList.stream()
                                    .filter(o -> o.getDateTime().isAfter(monthStartTime) && o.getDateTime().isBefore(endTime))
                                    .mapToDouble(OrderStatisticsDetailVO::getAmount).sum())
                    ));

            // 设置七日数据
            startDate = DateUtil.offsetWeek(DateUtil.date(), -1).toString("yyyy-MM-dd 00:00:00");
            LocalDateTime weekStartTime = LocalDateTime.parse(startDate, formatter);
            List<OrderStatisticsDetailVO> weekList = monthList.stream()
                    .filter(o -> o.getDateTime().isAfter(weekStartTime) && o.getDateTime().isBefore(endTime))
                    .collect(Collectors.toList());
            result.setWeekList(weekList);
            result.setWeekCount(weekList.stream().mapToLong(OrderStatisticsDetailVO::getCount).sum());
            result.setWeekAmount(
                    Double.parseDouble(format.format(
                            weekList.stream().mapToDouble(OrderStatisticsDetailVO::getAmount).sum())
                    ));

            // 设置今日数据
            LocalDateTime todayTime = LocalDateTime.parse(DateUtil.date().toString("yyyy-MM-dd 00:00:00"), formatter);
            result.setTodayCount(weekList.stream()
                    .filter(o -> o.getDateTime().equals(todayTime))
                    .mapToLong(OrderStatisticsDetailVO::getCount).sum());
            result.setTodayAmount(weekList.stream()
                    .filter(o -> o.getDateTime().equals(todayTime))
                    .mapToDouble(OrderStatisticsDetailVO::getAmount).sum());
        }

        // 查询近一年数据
        startDate = DateUtil.offset(DateUtil.date(), DateField.YEAR, -1).toString("yyyy-MM-01 00:00:00");
        endDate = DateUtil.date().toString("yyyy-MM-01 00:00:00");
        LocalDateTime yearStartTime = LocalDateTime.parse(startDate, formatter);
        List<OrderMonthReportDO> yearDOList = orderMonthReportRepository.getAllByDateTimeBetween(yearStartTime, endTime);
        timeList = TimeUtil.getBetweenMonths(startDate, endDate);
        List<OrderStatisticsDetailVO> yearList = convertOrderMonthStatistics(timeList, yearDOList);
        // 设置近一年数据
        result.setYearList(yearList);

        Long totalCount = orderMonthReportRepository.getAllCount();
        Double totalAmount = orderMonthReportRepository.getAllAmount();
        result.setTotalCount(totalCount != null ? totalCount : 0L);
        result.setTotalAmount(totalAmount != null ? Double.parseDouble(format.format(totalAmount)) : 0D);

        return Wrapper.success(result);
    }

    /**
     * 获取订单数据
     * @author 伍将
     * @date 2020/11/10
     * @param user:
     * @return com.ssy.lingxi.common.response.Wrapper<com.ssy.lingxi.report.model.vo.response.OrderStatisticsVO>
     **/
    @Override
    public Wrapper<OrderStatisticsVO> getOrderListByMember(UserLoginCacheDTO user) {
        OrderStatisticsVO result = new OrderStatisticsVO();

        DecimalFormat format = new DecimalFormat("#.00");

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String startDate;
        String endDate = DateUtil.date().toString("yyyy-MM-dd 23:59:59");
        LocalDateTime endTime = LocalDateTime.parse(endDate, formatter);
        List<String> timeList;

        // 查询近三十天数据
        startDate = DateUtil.offsetMonth(DateUtil.date(), -1).toString("yyyy-MM-dd 00:00:00");
        LocalDateTime monthStartTime = LocalDateTime.parse(startDate, formatter);
        List<StatisticsOrderBO> weekDOList = orderAddRecordDAO.getStatisticsDayOrderByMember(monthStartTime, endTime,user.getMemberId(),user.getMemberRoleId());

        timeList = TimeUtil.getBetweenDays(startDate, endDate);
        List<OrderStatisticsDetailVO> monthList = convertOrderDayStatisticsByBO(timeList, weekDOList);
        if (monthList.size() > 0) {

            // 设置三十日数据
            result.setMonthList(monthList);
            result.setMonthCount(monthList.stream()
                    .filter(o -> o.getDateTime().isAfter(monthStartTime) && o.getDateTime().isBefore(endTime))
                    .mapToLong(OrderStatisticsDetailVO::getCount).sum());
            result.setMonthAmount(
                    Double.parseDouble(format.format(
                            monthList.stream()
                                    .filter(o -> o.getDateTime().isAfter(monthStartTime) && o.getDateTime().isBefore(endTime))
                                    .mapToDouble(OrderStatisticsDetailVO::getAmount).sum())
                    ));

            // 设置七日数据
            startDate = DateUtil.offsetWeek(DateUtil.date(), -1).toString("yyyy-MM-dd 00:00:00");
            LocalDateTime weekStartTime = LocalDateTime.parse(startDate, formatter);
            List<OrderStatisticsDetailVO> weekList = monthList.stream()
                    .filter(o -> o.getDateTime().isAfter(weekStartTime) && o.getDateTime().isBefore(endTime))
                    .collect(Collectors.toList());
            result.setWeekList(weekList);
            result.setWeekCount(weekList.stream().mapToLong(OrderStatisticsDetailVO::getCount).sum());
            result.setWeekAmount(
                    Double.parseDouble(format.format(
                            weekList.stream().mapToDouble(OrderStatisticsDetailVO::getAmount).sum())
                    ));

            // 设置今日数据
            LocalDateTime todayTime = LocalDateTime.parse(DateUtil.date().toString("yyyy-MM-dd 00:00:00"), formatter);
            result.setTodayCount(weekList.stream()
                    .filter(o -> o.getDateTime().equals(todayTime))
                    .mapToLong(OrderStatisticsDetailVO::getCount).sum());
            result.setTodayAmount(weekList.stream()
                    .filter(o -> o.getDateTime().equals(todayTime))
                    .mapToDouble(OrderStatisticsDetailVO::getAmount).sum());
        }

        // 查询近一年数据
        startDate = DateUtil.offset(DateUtil.date(), DateField.YEAR, -1).toString("yyyy-MM-01 00:00:00");
        endDate = DateUtil.date().toString("yyyy-MM-01 00:00:00");
        LocalDateTime yearStartTime = LocalDateTime.parse(startDate, formatter);
        List<OrderMonthReportDO> yearDOList = orderMonthReportRepository.getAllByDateTimeBetween(yearStartTime, endTime);
        timeList = TimeUtil.getBetweenMonths(startDate, endDate);
        List<OrderStatisticsDetailVO> yearList = convertOrderMonthStatistics(timeList, yearDOList);
        // 设置近一年数据
        result.setYearList(yearList);

        Long totalCount = orderMonthReportRepository.getAllCount();
        Double totalAmount = orderMonthReportRepository.getAllAmount();
        result.setTotalCount(totalCount != null ? totalCount : 0L);
        result.setTotalAmount(totalAmount != null ? Double.parseDouble(format.format(totalAmount)) : 0D);

        return Wrapper.success(result);
    }

    /**
     * 转换订单日统计数据
     * @author 伍将
     * @date 2020/11/10
     * @param timeList:
     * @param reportDOList:
     * @return java.util.List<com.ssy.lingxi.report.model.vo.response.OrderStatisticsDetailVO>
     **/
    private List<OrderStatisticsDetailVO> convertOrderDayStatistics(List<String> timeList, List<OrderDayReportDO> reportDOList) {

        List<OrderStatisticsDetailVO> resultList = new ArrayList<>();

        if (timeList == null || timeList.size() <= 0) {
            return resultList;
        }

        Map<LocalDateTime, OrderDayReportDO> map = reportDOList.stream().collect(Collectors.toMap(OrderDayReportDO::getDateTime, Function.identity(), (key1, key2) -> key2));

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 补全时间
        for (String time : timeList) {
            OrderStatisticsDetailVO detailVO = new OrderStatisticsDetailVO();
            detailVO.setDateTime(LocalDateTime.parse(time + " 00:00:00", formatter));
            detailVO.setCount(0L);
            detailVO.setAmount(0D);
            if (map.containsKey(detailVO.getDateTime())) {
                detailVO.setCount(map.get(detailVO.getDateTime()).getCount());
                detailVO.setAmount(map.get(detailVO.getDateTime()).getAmount());
            }
            resultList.add(detailVO);
        }

        return resultList;
    }
    /**
     * 转换订单日统计数据
     * @author 伍将
     * @date 2020/11/10
     * @param timeList:
     * @param reportDOList:
     * @return java.util.List<com.ssy.lingxi.report.model.vo.response.OrderStatisticsDetailVO>
     **/
    private List<OrderStatisticsDetailVO> convertOrderDayStatisticsByBO(List<String> timeList, List<StatisticsOrderBO> reportDOList) {

        List<OrderStatisticsDetailVO> resultList = new ArrayList<>();

        if (timeList == null || timeList.size() <= 0) {
            return resultList;
        }

        Map<LocalDateTime, StatisticsOrderBO> map = reportDOList.stream().collect(Collectors.toMap(StatisticsOrderBO::getDateTime, Function.identity(), (key1, key2) -> key2));

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 补全时间
        for (String time : timeList) {
            OrderStatisticsDetailVO detailVO = new OrderStatisticsDetailVO();
            detailVO.setDateTime(LocalDateTime.parse(time + " 00:00:00", formatter));
            detailVO.setCount(0L);
            detailVO.setAmount(0D);
            if (map.containsKey(detailVO.getDateTime())) {
                detailVO.setCount(map.get(detailVO.getDateTime()).getCount());
                detailVO.setAmount(map.get(detailVO.getDateTime()).getAmount());
            }
            resultList.add(detailVO);
        }

        return resultList;
    }


    /**
     * 转换订单月统计数据
     * @author 伍将
     * @date 2020/11/10
     * @param timeList:
     * @param reportDOList:
     * @return java.util.List<com.ssy.lingxi.report.model.vo.response.OrderStatisticsDetailVO>
     **/
    private List<OrderStatisticsDetailVO> convertOrderMonthStatistics(List<String> timeList, List<OrderMonthReportDO> reportDOList) {

        List<OrderStatisticsDetailVO> resultList = new ArrayList<>();

        if (timeList == null || timeList.size() <= 0) {
            return resultList;
        }

        Map<LocalDateTime, OrderMonthReportDO> map = reportDOList.stream().collect(Collectors.toMap(OrderMonthReportDO::getDateTime, Function.identity(), (key1, key2) -> key2));

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 补全时间
        for (String time : timeList) {
            OrderStatisticsDetailVO detailVO = new OrderStatisticsDetailVO();
            detailVO.setDateTime(LocalDateTime.parse(time + " 00:00:00", formatter));
            detailVO.setCount(0L);
            detailVO.setAmount(0D);
            if (map.containsKey(detailVO.getDateTime())) {
                detailVO.setCount(map.get(detailVO.getDateTime()).getCount());
                detailVO.setAmount(map.get(detailVO.getDateTime()).getAmount());
            }
            resultList.add(detailVO);
        }

        return resultList;
    }

    /**
     * 新增订单
     * @author 伍将
     * @date 2021/1/14
     * @param addOrderVO : 请求参数
     **/
    @Transactional
    @Override
    public void addOrder(AddOrderVO addOrderVO) {

        // 如果订单金额大于0，则新增订单，订单数增加1,金额累加；反之为取消订单或审核不通过，订单数-1，金额减少；增减对应的时间仅为当前时间，补追溯原始来源时间
        Integer orderCount = addOrderVO.getAmount() > 0 ? 1 : -1;

        LocalDateTime time = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        OrderDayReportDO dayReport = orderDayReportRepository.findFirstByDateTime(time);
        if (null == dayReport) {
            dayReport = new OrderDayReportDO();
        }
        dayReport.setCount(dayReport.getCount() + orderCount);
        dayReport.setAmount(dayReport.getAmount() + addOrderVO.getAmount());
        orderDayReportRepository.saveAndFlush(dayReport);

        time = time.with(TemporalAdjusters.firstDayOfMonth());
        OrderMonthReportDO monthReport = orderMonthReportRepository.findFirstByDateTime(time);
        if (null == monthReport) {
            monthReport = new OrderMonthReportDO();
        }
        monthReport.setCount(monthReport.getCount() + orderCount);
        monthReport.setAmount(monthReport.getAmount() + addOrderVO.getAmount());
        orderMonthReportRepository.saveAndFlush(monthReport);
    }

    /**
     * 批量新增订单
     * @author 伍将
     * @date 2021/2/4
     * @param addOrderVO : 请求参数
     **/
    @Transactional
    @Override
    public void addOrderList(List<AddOrderVO> addOrderVO) {
        if (null != addOrderVO && addOrderVO.size() > 0) {

            // 结算取消订单数（订单金额小于0），订单金额小于0为取消订单
            int cancelCount = addOrderVO.stream().filter(o -> o.getAmount() < 0).toArray().length;
            int count = addOrderVO.size() - cancelCount;


            Double amount = addOrderVO.stream().mapToDouble(AddOrderVO::getAmount).sum();

            LocalDateTime time = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
            OrderDayReportDO dayReport = orderDayReportRepository.findFirstByDateTime(time);
            if (null == dayReport) {
                dayReport = new OrderDayReportDO();
            }
            dayReport.setCount(dayReport.getCount() + count);
            dayReport.setAmount(dayReport.getAmount() + amount);
            orderDayReportRepository.saveAndFlush(dayReport);

            time = time.with(TemporalAdjusters.firstDayOfMonth());
            OrderMonthReportDO monthReport = orderMonthReportRepository.findFirstByDateTime(time);
            if (null == monthReport) {
                monthReport = new OrderMonthReportDO();
            }
            monthReport.setCount(monthReport.getCount() + count);
            monthReport.setAmount(monthReport.getAmount() + amount);
            orderMonthReportRepository.saveAndFlush(monthReport);
        }
    }

}
